<?php
// +----------------------------------------------------------------------
// | INPHP
// | Copyright (c) 2023 https://inphp.cc All rights reserved.
// | Licensed ( https://opensource.org/licenses/MIT )
// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com
// +----------------------------------------------------------------------
// | PDO查询构建对象
// +----------------------------------------------------------------------
namespace Inphp\Core\Db\PDO;

use Closure;
use Inphp\Core\Db\Db;
use Inphp\Core\Util\Str;

class Query
{
    /**
     * 查询表名
     * @var mixed
     */
    public mixed $tableName;

    /**
     * 查询列
     * @var array
     */
    public array $columns = ["*"];

    /**
     * 关联查询
     * @var array
     */
    public array $joins = [];

    /**
     * 此查询对象关联其它查询对象时使用的别名
     * @var string
     */
    public string $as = "";

    /**
     * 条件
     * @var array
     */
    public array $wheres = [];

    /**
     * 分组条件
     * @var array
     */
    public array $groupBy = [];

    /**
     * 排序条件
     * @var array
     */
    public array $orderBy = [];

    /**
     * 侧重条件
     * @var array
     */
    public array $having = [];

    /**
     * 联合查询
     * @var array
     */
    public array $unions = [];

    /**
     * update/insert 字段和值
     * @var array
     */
    public array $sets = [];

    /**
     * 最后插入ID
     * @var array
     */
    public array $lastInsertId = [];

    /**
     * 影响行数
     * @var int
     */
    public int $affectRows = 0;

    /**
     * 语法对象
     * @var Grammar|null
     */
    private ?Grammar $grammar = null;

    /**
     * 初始化查询构建对象无需传入任何PDO连接对象
     * 因为只有查询真正提交执行的时候，才会去取得PDO对象
     * 数据库连接可以是实时连接的也可以是长连接，只有真正查询执行时，才会去使用连接对象，避免造成多余消耗
     */
    public function __construct()
    {

    }

    /**
     * 新建一个查询对象
     * @return Query
     */
    public function newQuery(): Query
    {
        return new Query();
    }

    /**
     * 查询开始
     * SELECT/DELETE/UPDATE/INSERT ...
     * @param string|Query|Model $table 表对象/名称
     * @param string $as                查询别名
     * @return Query
     */
    public function from(string|Query|Model $table, string $as = ""): Query
    {
        if ($table instanceof Query) {
            $this->tableName = [
                "query" => $table,
                "as"    => $as
            ];
        } else {
            $this->tableName = (string)$table . (!empty($as) ? " {$as}" : "");
        }
        return $this;
    }

    /**
     * SELECT 字段
     * @param string|array $columns 字段列表
     * @param bool $replace         是否要替换已有字段列表
     * @return Query
     */
    public function select(string|array $columns, bool $replace = true): Query
    {
        $columns = is_array($columns) ? $columns : explode(",", $columns);
        $list = [];
        //处理替换神奇的分隔符号
        foreach ($columns as $column) {
            //将多个连续空格替换为单个空格
            $column = preg_replace("/\s+/", " ", $column);
            //替换分隔符号
            $column = str_replace("、", ",", $column);
            $column = str_replace("，", ",", $column);
            $column = str_replace("|", ",", $column);
            //去除两边的空格
            $list[] = trim($column);
        }
        //如果需要合并，自动处理去重
        $this->columns = $replace ? $list : array_unique(array_merge($this->columns, $list));
        return $this;
    }

    /**
     * 增加查询字段
     * @param string|array $columns
     * @return Query
     */
    public function addSelect(string|array $columns): Query
    {
        return $this->select($columns, false);
    }

    /**
     * 关联查询
     * @param string|Query|Closure $tableName
     * @param string $on
     * @param string $type
     * @return Query
     */
    public function join(string|Query|Closure $tableName, string $on, string $type = "inner"): Query
    {
        $nested = false;
        $as = "";
        if (!is_string($tableName)) {
            //属于嵌套查询
            $nested = true;
            if ($tableName instanceof Closure) {
                //执行匿名函数
                $query = $this->newQuery();
                call_user_func($tableName, $query);
            }
            //编译成查询语句
            list($queryString, $params) = $tableName->getGrammar()->compileQuery();
            $as = !empty($tableName->as) ? Str::trim($tableName->as) : ("tmp".rand(0, 999999));
            $as = !empty($as) ? $as : ("tmp".rand(0, 999999));
            $table = "($queryString)";
            //将参数归纳
            $this->addParams($params);
        }else {
            //只是个表名字符串
            $table = $tableName;
        }
        $this->joins[] = compact("table", "as", "on", "type", "nested");
        return $this;
    }

    /**
     * 左关联
     * @param string|Query|Closure $tableName
     * @param string $on
     * @return $this
     */
    public function leftJoin(string|Query|Closure $tableName, string $on): Query
    {
        return $this->join($tableName, $on, "left");
    }

    /**
     * 右关联
     * @param string|Query|Closure $tableName
     * @param string $on
     * @return $this
     */
    public function rightJoin(string|Query|Closure $tableName, string $on): Query
    {
        return $this->join($tableName, $on, "right");
    }

    /**
     * 别名
     * @param string $as
     * @return Query
     */
    public function as(string $as): Query
    {
        $this->as = $as;
        return $this;
    }

    /**
     * where
     * @param string|Closure $column
     * @param string|Closure $operator
     * @param string|Closure|null $value
     * @param bool $or
     * @return Query
     */
    public function where(string|Closure $column, string|Closure $operator = "=", string|Closure|null $value = null, bool $or = false): Query
    {
        if (func_num_args() === 2) {
            //仅有2个参数的时候，对比符号是 = 号
            list($operator, $value) = ["=", $operator];
        }
        if ($column instanceof Closure) {
            //嵌套查询条件 where
            return $this->whereNested($column, $or);
        }
        if ($value instanceof Closure) {
            //子查询 where $column $operator ($value sql) ...
            return $this->whereSub($column, $operator, $value, $or);
        }
        if (is_null($value)) {
            //空值条件
            return $this->whereNull($column, $or);
        }
        $type = "default";
        $this->wheres[] = compact("type", "column", "operator", "value", "or");
        return $this;
    }

    /**
     * or where
     * @param string|Closure $column
     * @param string|Closure $operator
     * @param string|Closure|null $value
     * @return Query
     */
    public function orWhere(string|Closure $column, string|Closure $operator, string|Closure|null $value = null): Query
    {
        if (func_num_args() === 2) {
            //仅有2个参数的时候，对比符号是 = 号
            list($operator, $value) = ["=", $operator];
        }
        return $this->where($column, $operator, $value, true);
    }

    /**
     * 直接SQL条件
     * @param string $sql
     * @param bool $or
     * @return Query
     */
    public function whereRaw(string $sql, bool $or = false): Query
    {
        $type = "raw";
        $this->wheres[] = compact("type", "sql", "or");
        return $this;
    }

    /**
     * 直接SQL条件
     * @param string $sql
     * @return Query
     */
    public function orWhereRaw(string $sql): Query
    {
        return $this->whereRaw($sql, true);
    }

    /**
     * 子查询条件
     * @param string $column
     * @param string $operator
     * @param Closure $closure
     * @param bool $or
     * @return Query
     */
    private function whereSub(string $column, string $operator, Closure $closure, bool $or = false): Query
    {
        $type = "sub";
        $query = $this->newQuery();
        call_user_func($closure, $query);
        $this->wheres[] = compact("type", "column", "operator", "query", "or");
        return $this;
    }

    /**
     * 嵌套查询条件
     * @param Closure $closure
     * @param bool $or
     * @return Query
     */
    private function whereNested(Closure $closure, bool $or = false): Query
    {
        $query = $this->newQuery();
        $query->from($this->tableName);
        call_user_func($closure, $query);
        if (!empty($query->wheres)) {
            $type = "nested";
            $this->wheres[] = compact("type", "query", "or");
        }
        return $this;
    }

    /**
     * 字段值为 null 或 非null
     * @param string $column
     * @param bool $not
     * @param bool $or
     * @return $this
     */
    public function whereNull(string $column, bool $not = false, bool $or = false): Query
    {
        return $this->whereRaw("{$column} ".($not ? "is not" : "is")." null", $or);
    }
    public function whereNotNull(string $column, bool $or = false): Query
    {
        return $this->whereNull($column, true, $or);
    }
    public function orWhereNull(string $column, bool $not = false): Query
    {
        return $this->whereNull($column, $not, true);
    }
    public function orWhereNotNull(string $column): Query
    {
        return $this->orWhereNull($column, true);
    }

    /**
     * 字段值不为空值 或 为空值 $column="" or $column is null
     * @param string $column
     * @param bool $not
     * @param bool $or
     * @return Query
     */
    public function whereEmpty(string $column, bool $not = false, bool $or = false): Query
    {
        return $this->where(function(Query $q) use($column, $not) {
            $q->where($column, $not ? "!=" : "=", "");
            if ($not) {
                $q->whereNotNull($column);
            } else {
                $q->orWhereNull($column);
            }
        }, "=", null, $or);
    }

    /**
     * 字段值可能为空 or ($column="" or $column is null)
     * @param string $column
     * @param bool $not
     * @return $this
     */
    public function orWhereEmpty(string $column, bool $not = false): Query
    {
        return $this->whereEmpty($column, $not, true);
    }

    /**
     * 字段值不为空 $column != "" and $column not is null
     * @param string $column
     * @param bool $or
     * @return Query
     */
    public function whereNotEmpty(string $column, bool $or = false): Query
    {
        return $this->whereEmpty($column, true, $or);
    }

    /**
     * 或者字段值不为空 or ($column != "" and $column not is null)
     * @param string $column
     * @return Query
     */
    public function orWhereNotEmpty(string $column): Query
    {
        return $this->whereNotEmpty($column, true);
    }

    /**
     * where in
     * @param string $column
     * @param string|array|Closure $value
     * @param bool $not
     * @param bool $or
     * @return Query
     */
    public function whereIn(string $column, string|array|Closure $value, bool $not = false, bool $or = false): Query
    {
        $type = "in";
        if ($value instanceof Closure) {
            $query = $this->newQuery();
            call_user_func($value, $query);
            $value = $query;
        }
        $this->wheres[] = compact("type", "column", "value", "not", "or");
        return $this;
    }
    public function whereNotIn(string $column, string|array|Closure $value, bool $or = false): Query
    {
        return $this->whereIn($column, $value, true, $or);
    }
    public function orWhereIn(string $column, string|array|Closure $value, bool $not = false): Query
    {
        return $this->whereIn($column, $value, $not, true);
    }
    public function orWhereNotIn(string $column, string|array|Closure $value): Query
    {
        return $this->orWhereIn($column, $value, true);
    }

    /**
     * where $column [not] between $section[0] and $section[1]
     * @param string $column    字段名
     * @param array $value    区间值
     * @param bool $not         在/不在
     * @param bool $or          or/and 默认是 and连接
     * @return Query
     */
    public function whereBetween(string $column, array $value, bool $not = false, bool $or = false): Query
    {
        $type = "between";
        $this->wheres[] = compact("type", "column", "value", "not", "or");
        return $this;
    }
    public function whereNotBetween(string $column, array $value, bool $or = false): Query
    {
        return $this->whereBetween($column, $value, true, $or);
    }
    public function orWhereBetween(string $column, array $value, bool $not = false): Query
    {
        return $this->whereBetween($column, $value, $not, true);
    }
    public function orWhereNotBetween(string $column, array $value): Query
    {
        return $this->orWhereBetween($column, $value, true);
    }

    /**
     * where exists
     * @param Closure $closure
     * @param bool $not
     * @param bool $or
     * @return Query
     */
    public function whereExists(Closure $closure, bool $not = false, bool $or = false): Query
    {
        $type = "exists";
        $query = $this->newQuery();
        call_user_func($closure, $query);
        $this->wheres[] = compact("type", "query", "not", "or");
        return $this;
    }
    public function whereNotExists(Closure $closure, bool $or = false): Query
    {
        return $this->whereExists($closure, true, $or);
    }
    public function orWhereExists(Closure $closure, bool $not = false): Query
    {
        return $this->whereExists($closure, $not, true);
    }
    public function orWhereNotExists(Closure $closure, bool $or = false): Query
    {
        return $this->orWhereExists($closure, true);
    }

    /**
     * where FIND_IN_SET($value, $column)
     * @param string $value
     * @param string $column
     * @param bool $or
     * @return Query
     */
    public function whereFindInSet(string $value, string $column, bool $or = false): Query
    {
        $type = "findInSet";
        $this->wheres[] = compact("type", "column", "value", "or");
        return $this;
    }
    public function orWhereFindInSet(string $value, string $column): Query
    {
        return $this->whereFindInSet($value, $column, true);
    }

    /**
     * where like
     * @param string $column        字段名
     * @param string $value         匹配值
     * @param string $side          边界
     * @param bool $not             like/not like
     * @param bool $or              or/and
     * @return Query
     */
    public function whereLike(string $column, string $value, string $side = "all", bool $not = false, bool $or = false): Query
    {
        $type = "like";
        $this->wheres[] = compact("type", "column", "value", "side", "not", "or");
        return $this;
    }
    public function whereLeftLike(string $column, string $value, bool $not = false, bool $or = false): Query
    {
        return $this->whereLike($column, $value, "left", $not, $or);
    }
    public function whereRightLike(string $column, string $value, bool $not = false, bool $or = false): Query
    {
        return $this->whereLike($column, $value, "right", $not, $or);
    }
    public function whereNotLike(string $column, string $value, string $side = "all", bool $or = false): Query
    {
        return $this->whereLike($column, $value, $side, true, $or);
    }
    public function whereLeftNotLike(string $column, string $value, bool $or = false): Query
    {
        return $this->whereLeftLike($column, $value, true, $or);
    }
    public function whereRightNotLike(string $column, string $value, bool $or = false): Query
    {
        return $this->whereRightLike($column, $value, true, $or);
    }
    public function orWhereLike(string $column, string $value, string $side = "all", bool $not = false): Query
    {
        return $this->whereLike($column, $value, $side, $not, true);
    }
    public function orWhereLeftLike(string $column, string $value, bool $not = false): Query
    {
        return $this->whereLeftLike($column, $value, $not, true);
    }
    public function orWhereRightLike(string $column, string $value, bool $not = false): Query
    {
        return $this->whereRightLike($column, $value, $not, true);
    }
    public function orWhereNotLike(string $column, string $value, string $side = "all"): Query
    {
        return $this->whereNotLike($column, $value, $side, true);
    }
    public function orWhereLeftNotLike(string $column, string $value): Query
    {
        return $this->whereLeftNotLike($column, $value, true);
    }
    public function orWhereRightNotLike(string $column, string $value): Query
    {
        return $this->whereRightNotLike($column, $value, true);
    }

    /**
     * 判断以 逗号 , 分隔的字符与字段值是否 有交集
     * @param string $column
     * @param string|array $value
     * @param bool $not
     * @return Query
     */
    public function whereIsIntersection(string $column, string|array $value, bool $not = false) : Query
    {
        $value = is_array($value) ? join(",", $value) : $value;
        if(!empty($value) && !empty($column)){
            return $this->whereRaw("(select concat('{$value}', ',') regexp concat(replace({$column}, ',', ',|'), ',')) = ".($not ? "0" : "1"));
        }
        return $this;
    }

    /**
     * 判断以 逗号 , 分隔的字符与字段值是否 无交集
     * @param string $column
     * @param string|array $value
     * @return $this
     */
    public function whereNotIntersection(string $column, string|array $value) : Query
    {
        return $this->whereIsIntersection($column, $value, true);
    }

    /**
     * 判断以 逗号分隔的字段值里，是否包含number里边的值，自动处理numbers
     * @param string|array $numbers
     * @param string $column
     * @param bool $or
     * @return Query
     */
    public function whereNumberInSet(string|array $numbers, string $column, bool $or = false): Query
    {
        if (!is_array($numbers)) {
            $numbers = !empty($numbers) ? Str::trim($numbers) : "";
            $numbers = !empty($numbers) ? explode(",", $numbers) : [];
        }
        if (!empty($numbers)) {
            $numberList = [];
            foreach ($numbers as $number) {
                if (is_numeric($number) && !in_array(ceil($number), $numberList)) {
                    $numberList[] = ceil($number);
                }
            }
            if (!empty($numberList)) {
                if(count($numberList) === 1) {
                    $this->whereFindInSet($numberList[0], $column, $or);
                } else {
                    //将包含这些分组ID的都列出来
                    $this->whereRaw("{$column} regexp '(^|,)(".join("|", $numberList).")(,|$)'", $or);
                }
            }
        }
        return $this;
    }

    /**
     * 分组
     * @param string|array $columns
     * @return Query
     */
    public function groupBy(string|array $columns): Query
    {
        $columns = is_array($columns) ? $columns : explode(",", Str::trim($columns));
        $this->groupBy = array_unique(array_merge($this->groupBy, $columns));
        return $this;
    }

    /**
     * 排序
     * @param string $column
     * @param string $type
     * @return Query
     */
    public function orderBy(string $column, string $type = "ASC"): Query
    {
        $this->orderBy[] = compact("column", "type");
        return $this;
    }

    /**
     * union
     * @param Query $query
     * @param bool $all
     * @return Query
     */
    public function union(Query $query, bool $all = false) : Query
    {
        $this->unions[] = compact('query', 'all');
        return $this;
    }

    /**
     * union all
     * @param Query $query
     * @return Query
     */
    public function unionAll(Query $query) : Query
    {
        return $this->union($query);
    }

    /**
     * 侧重查询
     * @param string $column
     * @param string $operator
     * @param string|null $value
     * @return Query
     */
    public function having(string $column, string $operator = "=", ?string $value = null) : Query
    {
        if (func_num_args() == 2) {
            list($value, $operator) = [$operator, '='];
        }
        $this->having[] = compact('column', 'operator', 'value');
        return $this;
    }

    /************************
     * 下方主要是查询的执行方法 *
     ************************/

    /**
     * 获取select查询结果
     * @param int $total
     * @param int $offset
     * @return array
     */
    public function get(int $total = 0, int $offset = 0): array
    {
        $total = max(0, $total);
        $total = ceil($total);
        $offset = max(0, $offset);
        $offset = ceil($offset);
        list($queryString, $params) = $this->compile();
        if ($total > 0) {
            $queryString .= " limit {$offset},{$total}";
        }
        $result = Db::query($queryString, $params, Db::READ, null, "select");
        return is_array($result) ? $result : [];
    }

    /**
     * 获取前N行数据
     * @param int $total
     * @return array
     */
    public function top(int $total = 1): array
    {
        return $this->get($total);
    }

    /**
     * 获取第一条数据
     * @return array|null
     */
    public function first(): array|null
    {
        $row = $this->get(1);
        return !empty($row) ? reset($row) : null;
    }

    /**
     * 取一个单一字段的结果集
     * @param string $column
     * @param bool $distinct
     * @param int $total
     * @param int $offset
     * @return array
     */
    public function lists(string $column, bool $distinct = false, int $total = 0, int $offset = 0): array
    {
        $this->columns = [($distinct ? "distinct " : "")."{$column} as listColumn"];
        $rows = $this->get($total, $offset);
        $list = [];
        foreach ($rows as $row) {
            if (isset($row["listColumn"])) {
                $list[] = $row["listColumn"];
            }
        }
        return $list;
    }

    /**
     * 获取一共有多少行数据
     * @return int
     */
    public function rows(): int
    {
        $columns = $this->columns;
        $check = $this->hasAggregateFunction() || !empty($this->groupBy);
        if (!$check) {
            $this->columns = ["count(*) as num"];
        }
        list($queryString, $params) = $this->compile();
        if ($check) {
            $queryString = "SELECT COUNT(*) as num FROM ({$queryString}) TEMP_TABLE";
        }
        $this->columns = $columns;
        if ($result = Db::query($queryString, $params, Db::READ, null, "select")) {
            return is_array($result) ? ($result[0]["num"] ?? 0) : 0;
        }
        return 0;
    }

    /**
     * 检测是否存在统计函数
     * @return bool
     */
    public function hasAggregateFunction(): bool
    {
        $columnsString = join(",", $this->columns);
        return strripos($columnsString, "sum(") > 0
            || strripos($columnsString, "avg(") > 0
            || strripos($columnsString, "count(") > 0
            || strripos($columnsString, "max(") > 0
            || strripos($columnsString, "min(") > 0;
    }

    /**
     * 统计
     * @param string $column
     * @param string $fn
     * @return float|int|null
     */
    private function aggregate(string $column, string $fn): float|int|null
    {
        $column = Db::getConnection()->parseColumns($column);
        $this->columns = ["{$fn}({$column}) as val"];
        $row = $this->first();
        return $row ? ($row["val"] ?? null) : null;
    }

    /**
     * 取最大值
     * @param string $column
     * @return float|int|null
     */
    public function max(string $column): float|int|null
    {
        return $this->aggregate($column, "max");
    }

    /**
     * 取最小值
     * @param string $column
     * @return float|int|null
     */
    public function min(string $column): float|int|null
    {
        return $this->aggregate($column, "min");
    }

    /**
     * 取最小值
     * @param string $column
     * @return float|int|null
     */
    public function avg(string $column): float|int|null
    {
        return $this->aggregate($column, "avg");
    }

    /**
     * 取总和
     * @param string $column
     * @return float|int|null
     */
    public function sum(string $column): float|int|null
    {
        return $this->aggregate($column, "sum");
    }

    /************************
     * 下方主要是修改数据库方法 *
     ************************/

    /**
     * insert/update set
     * @param string|array $column
     * @param mixed|null $value
     * @param bool $raw
     * @return Query
     */
    public function set(string|array $column, mixed $value = null, bool $raw = false): Query
    {
        //由于 insert 支持多条一次插入，所以传进来的 $column 有可能是 2 维数组，所以统一处理为 2 维数据， update 取第一个即可
        if (is_array($column)) {
            $columns = is_array(reset($column)) ? $column : [$column];
            foreach ($columns as $index => $column) {
                foreach ($column as $fieldName => $value) {
                    $this->sets[$index][] = [
                        "field"     => $fieldName,
                        "value"     => $value,
                        "raw"       => $value === true || $value === false ? $value : $raw
                    ];
                }
            }
        } else {
            //默认加到第一个set
            $this->sets[0][] = [
                "field" => $column,
                "value" => $value,
                "raw"   => $raw
            ];
        }
        return $this;
    }

    /**
     * 设置无需处理的值
     * @param string $column
     * @param mixed $value
     * @return Query
     */
    public function setRaw(string $column, mixed $value): Query
    {
        return $this->set($column, $value, true);
    }

    /**
     * 自增值
     * @param string $column
     * @param float|int $number
     * @param bool $decrement
     * @return Query
     */
    public function increment(string $column, float|int $number = 1, bool $decrement = false): Query
    {
        if ($number === 0) {
            return $this;
        }
        return $this->setRaw($column, "`{$column}`".($decrement ? "-" : "+").$number);
    }

    /**
     * 减值
     * @param string $column
     * @param float|int $number
     * @return Query
     */
    public function decrement(string $column, float|int $number = 1): Query
    {
        return $this->increment($column, $number, true);
    }

    /**
     * update ....
     * @param array|null $columns
     * @return bool
     */
    public function update(?array $columns = null): bool
    {
        if (is_array($columns)) {
            $this->set($columns);
        }
        return $this->execute("update");
    }

    /**
     * insert into ....
     * @param array|null $sets
     * @return bool
     */
    public function insert(?array $sets = null): bool
    {
        if (!empty($sets)) {
            $this->sets = [];
            $this->set($sets);
        }
        list($queryString, $params) = $this->compile("insert");
        //因可能存在多条数据同时插入，但为了保全数据的完整性，使用事务处理， 如果外部定义了事务，则不再重复使用事务
        $needTransaction = !Db::inTransaction();
        if ($needTransaction) {
            Db::begin();
        }
        foreach ($queryString as $key => $sql) {
            if (!$this->execute("insert", $sql, $params[$key] ?? null)) {
                if ($needTransaction) {
                    Db::rollback();
                }
                return false;
            }
        }
        if ($needTransaction) {
            Db::commit();
        }
        return true;
    }

    /**
     * 执行条件式插入，当条件数据不存在时插入，仅可插入一条记录，不成功就是失败，不需要在此定义事务
     * @return bool
     */
    public function insertIfNotExists() : bool
    {
        return $this->execute('insert where not exists');
    }

    /**
     * 清空表，重新定义自增 ID
     * @return bool
     */
    public function truncate() : bool
    {
        return $this->execute('truncate');
    }

    /**
     * delete from ....
     * @return bool
     */
    public function delete(): bool
    {
        return $this->execute("delete");
    }

    /**
     * 执行增删改
     * @param string $type              执行类型 insert delete update ...
     * @param string|null $queryString  SQL
     * @param array|null $params        参数值
     * @return bool                     执行是否成功
     */
    private function execute(string $type, ?string $queryString = null, ?array $params = null): bool
    {
        if (is_null($queryString)) {
            list($queryString, $params) = $this->compile($type);
        }
        if ($result = Db::query($queryString, $params, Db::WRITE, null, "execute")) {
            $this->affectRows += $result[0];
            if(strripos($type, "insert") === 0){
                $this->lastInsertId[] = $result[1];
            }
            return true;
        }
        return false;
    }

    /**
     * 获取最后一个ID
     * @return mixed
     */
    public function getLastInsertId(): mixed
    {
        return count($this->lastInsertId) > 1 ? $this->lastInsertId : ($this->lastInsertId[0] ?? 0);
    }

    /**
     * 获取影响行数
     * @return int
     */
    public function getAffectRows(): int
    {
        return $this->affectRows;
    }

    /**
     * 获取语法处理
     * @return Grammar
     */
    public function getGrammar(): Grammar
    {
        if (!$this->grammar) {
            $this->grammar = new Grammar($this);
        }
        return $this->grammar;
    }

    /**
     * 编译处理
     * @param string $type
     * @return array
     */
    public function compile(string $type = "select"): array
    {
        return $this->getGrammar()->compileQuery($type);
    }
}